// 创建线程
#include <thread>
#include <iostream>
#include <functional>
#include <vector>
#include <memory>

using std::cout;
using std::endl;

void func1(){
    static int i = 0;
    i++;
    std::cout << i << std::endl;
}
void func(int a, double b){}

void f_sleep()
{
    std::this_thread::sleep_for(std::chrono::seconds(3));
    cout << "Time Out" << endl;
}

std::vector<std::thread> g_list;
std::vector<std::shared_ptr<std::thread>> g_list2;

void CreateThread()
{
    //创建线程 使用 move 移动线程对象
    std::thread t(func1);
    g_list.push_back(std::move(t)); 

    //创建以智能指针的方式来创建线程并塞入容器中
    g_list2.push_back(std::make_shared<std::thread>(func1));

    std::thread r(func1);
    r.detach(); //分离线程
}

int main()
{
    {
        // 使用 std::thread 创建线程，只需要提供函数或函数对象，并且可以同时指定线程函数的参数
        //常规的线程初始化
        std::thread t(func, 1, 2);
        t.join();
        //std::thread t1(std::move(t)); //线程被移动之后，线程对象  t 将不再代表任何线程 // 线程不能重复join
        //t1.join();
    }

    {
        //使用bind 和 lambda 表达式来创建线程
        std::thread t1(std::bind(func, 1, 2));
        std::thread t2([](int a, double b){}, 1, 2);
        t1.join();
        t2.join();
    }

    {
        //注意线程对象的声明周期 ，因为线程对象可能先于线程函数结束，应该保证线程对象的生命周期在线程函数执行完仍然存在。
        //方式：1. 使用join 方式来阻塞等待线程函数执行完
              //2. 通过 detach 方式让线程在后台运行
              //3. 将线程对象保存到一个容器中，以保证线程对象的生命周期
        //std::thread t(func1); //terminate called without an active exception


        CreateThread();
        for(auto & thread : g_list)
        {
            thread.join();
        }

        for(auto & thread : g_list2)
        {
            thread->join();
        }

    }
    {
        //获取线程相关信息
        std::thread t(func, 1, 2);

        std::cout << t.get_id() << std::endl; //获取当前线程ID
        std::cout << std::thread::hardware_concurrency()  << "\t" << __LINE__ << std::endl;
        t.join(); //只有线程运行时，才能得到线程ID
    }
    
    {
        //线程休眠
        std::thread t(f_sleep);
        t.join();
    }
    return 0;
}
